package com.example.thpool2;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class LockThreadPool extends Thread {
    private static Queue<Worker> WORKER_QUEUE;
    private static Queue<Runnable> TASK_QUEUE;
    private static ReentrantLock lock = new ReentrantLock();
    private static Condition getCondition = lock.newCondition();
    private static Condition addCondition = lock.newCondition();
    private final static ThreadGroup THREAD_GROUP = new ThreadGroup("MyThreadGroup");
    private final static String THREAD_POOL_NAME = "MY_THREAD_POOL_";
    private int min;
    private int active;
    private int max;
    private int taskQueueSize;
    private DiscardPolicy discardPolicy;
    private static volatile boolean terminated = false;

    public LockThreadPool() {
        this(4, 8, 12, 200, (task) -> {
            System.out.println("task discarded: " + task);
        });
    }

    public LockThreadPool(int min, int active, int max, int taskQueueSize, DiscardPolicy discardPolicy) {
        this.min = min;
        this.active = active;
        this.max = max;
        this.taskQueueSize = taskQueueSize;

        WORKER_QUEUE = new LinkedList<>();
        TASK_QUEUE = new LinkedList<>();
        this.discardPolicy = discardPolicy;
    }

    public void createWorker() {
        Worker worker = new Worker(THREAD_GROUP, nextSeq());
        WORKER_QUEUE.offer(worker);
        worker.start();
    }

    public boolean submitTask(Runnable task) {
        if (terminated) {
            throw new IllegalStateException("thread pool Already terminated");
        }
        lock.lock();
        if (TASK_QUEUE.size() < taskQueueSize) {
            TASK_QUEUE.add(task);
            addCondition.signalAll();
        } else {
            discardPolicy.discard(task);
            return false;
        }
        lock.unlock();
        return true;
    }

    public String nextSeq() {
        return THREAD_POOL_NAME + UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
    }

    public void init() {
        for (int i = 0; i < min; i++) {
            createWorker();
        }
    }

    @Override
    public void run() {
        init();
        while (!isInterrupted()) {
            try {
                TimeUnit.SECONDS.sleep(1);
                System.out.printf("thread pool min: %d, active: %d, max: %d, current: %d, task: %d\n",
                        min, active, max, WORKER_QUEUE.size(), TASK_QUEUE.size());

//                lock.lock();
                if (TASK_QUEUE.size() > min && WORKER_QUEUE.size() < active) {
                    createWorker();
                } else if (TASK_QUEUE.size() > 2 * min && WORKER_QUEUE.size() < max) {
                    createWorker();
                }

                if (TASK_QUEUE.size() < max && WORKER_QUEUE.size() > active) {
                    Iterator<Worker> iterator = WORKER_QUEUE.iterator();
                    while (iterator.hasNext()) {
                        Worker w = iterator.next();
                        if (WorkerState.FREE == w.getWorkState()) {
                            iterator.remove();
                            w.close();
                            System.out.println("remove worker: " + w.getName());
                            break;
                        }
                    }
                }
//                lock.unlock();

            } catch (InterruptedException e) {
                break;
            }
        }
        System.out.println("thread pool terminated");
    }

    public void shutdown() {
        terminated = true;
        System.out.println("waiting task execute...");
        lock.lock();
        while (!TASK_QUEUE.isEmpty()) {
            try {
                getCondition.await();
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        lock.unlock();
        System.out.println("waiting worker execute...");
        while (!WORKER_QUEUE.isEmpty()) {
            Iterator<Worker> iterator = WORKER_QUEUE.iterator();
            while (iterator.hasNext()) {
                Worker w = iterator.next();
                if (WorkerState.FREE == w.getWorkState()) {
                    iterator.remove();
                    w.close();
                }
            }
        }
        this.interrupt();
    }

    private enum WorkerState {
        FREE, RUNNING, TERMINATED
    }

    private static class Worker extends Thread {

        private WorkerState state;

        public Worker(ThreadGroup group, String name) {
            super(group, name);
            state = WorkerState.FREE;
        }

        public WorkerState getWorkState() {
            return state;
        }

        @Override
        public void run() {
            while (state != WorkerState.TERMINATED && !isInterrupted()) {
                try {
                    lock.lock();
                    while (TASK_QUEUE.isEmpty()) {
                        addCondition.await();
                    }
                    Runnable task = TASK_QUEUE.poll();
                    getCondition.signalAll();
                    lock.unlock();

                    state = WorkerState.RUNNING;
                    task.run();
                    state = WorkerState.FREE;
                } catch (Exception e) {
                    System.out.println("worker interrupted or closed");
                    state = WorkerState.TERMINATED;
                }
            }
        }

        public void close() {
            state = WorkerState.TERMINATED;
            this.interrupt();
        }
    }

    public interface DiscardPolicy {
        void discard(Runnable task);
    }
}
